package dk.itu.pervasive.jcaf;

import java.util.Observable;
import java.util.Observer;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

public class Visitor_At_ITU_Activity extends Activity implements Observer {

	private UIData uiData;
	private static Context context;
	private LocationManager locationManager;
	private LocationListener locationListener;
	private boolean removeListening = false;
	private Location currentBestLocation;
	private String bt_id;

	private class UIData {
		public String screenText = "";
	}

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		this.uiData = new UIData();
		this.bt_id = getBluetoothID();
		Visitor_At_ITU_Activity.context = this.getApplicationContext();
		getAndroidLocation();
	}

	public static Context getAppContext() {
		return Visitor_At_ITU_Activity.context;
	}

	private String getBluetoothID() {
		String bt_id_temp = "NO BLUETOOTH ID SET";
		try {
			BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter();
			if (bluetooth != null) {
				bt_id_temp = bluetooth.getAddress();
				Log.i("bt", "bt_id_temp: " + bt_id_temp);
				bt_id_temp = bt_id_temp.replaceAll(":", "");
				Log.i("bt", "bt_id_temp no colon: " + bt_id_temp);
				// TODO: add zeros if too few digits ?
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return bt_id_temp;
	}

	public void clearDisplay(View v) {
		uiData.screenText = "";
		updateDisplay();
	}

	public void getLocation(View v) {
		getAndroidLocation();
	}

	public void notifyArrival(View v) {
		automaticNotifyArrival();
	}

	private void automaticNotifyArrival() {
		uiData.screenText += "\nConnecting to JCAF at ITU ...";
		removeListening = true;
		updateDisplay();

		AsyncConnection connection = new AsyncConnection(this, this.bt_id);
		new Thread(connection).start();
	}

	private void updateDisplay() {
		this.runOnUiThread(new Runnable() {
			public void run() {
				TextView textView = (TextView) findViewById(R.id.mainTextView);
				textView.setText(uiData.screenText);
			}
		});
		// textView.setText("Location: " + location);
		if (removeListening) {
			locationManager.removeUpdates(locationListener);
			removeListening = false;
		}
	}

	@Override
	public void update(Observable observable, Object data) {
		// TODO Auto-generated method stub
		Log.i("mapou", "update() " + data);
		String castData = (String) data;
		uiData.screenText += castData;
		updateDisplay();
	}

	@Override
	protected void onPause() {
		this.finish();
		super.onPause();
	}

	private void getAndroidLocation() {

		// Acquire a reference to the system Location Manager
		locationManager = (LocationManager) this
				.getSystemService(Context.LOCATION_SERVICE);

		// Define a listener that responds to location updates
		locationListener = new LocationListener() {
			public void onLocationChanged(Location location) {
				// Called when a new location is found by the network
				// location provider.

				if (isBetterLocation(location, currentBestLocation)) {
					currentBestLocation = location;
					Log.i("mapou", "new location is better");
				} else {
					Log.i("mapou", "new location is NOT better");
				}

				String lat = Double.toString(currentBestLocation.getLatitude());
				String lon = Double
						.toString(currentBestLocation.getLongitude());
				Log.i("mapou", "A_alt: " + lat + " , A_lat: " + lon);

				String newLocation = "A_lat. "
						+ currentBestLocation.getLatitude() + "\nA_long. "
						+ currentBestLocation.getLongitude();

				if (uiData.screenText.length() < 1) {
					newLocation = "Location:\n" + newLocation;
				} else {
					newLocation = "\n" + "Location:\n" + newLocation;
				}

				uiData.screenText += newLocation;
				// removeListening = true;
				updateDisplay();

				if (nearITU(currentBestLocation)) {
					automaticNotifyArrival();
				}

			}

			public void onStatusChanged(String provider, int status,
					Bundle extras) {
			}

			public void onProviderEnabled(String provider) {
			}

			public void onProviderDisabled(String provider) {
			}

		};

		// Register the listener with the Location Manager to receive location
		// updates
		locationManager.requestLocationUpdates(
				LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
		// locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
		// 0, 0, locationListener);

	}

	protected boolean nearITU(Location currentlyBestLocation) {
		// ITU_lat, ITU_long were found here: http://itouchmap.com/latlong.html
		final double ITU_lat = 55.659601;
		final double ITU_long = 12.591126;
		final double testRadius = 0.003; // 0.005 ~ 930m | 0.001 ~ 186m |
											// http://www.csgnetwork.com/gpsdistcalc.html

		currentlyBestLocation.getLatitude();
		currentlyBestLocation.getLongitude();

		double dist = Math
				.sqrt(Math.pow(ITU_lat - currentlyBestLocation.getLatitude(),
						2.0)
						+ Math.pow(
								ITU_long - currentlyBestLocation.getLongitude(),
								2.0));

		Log.i("mapou", "Distance to ITU: " + dist);

		if (dist < testRadius) {
			return true;
		} else {
			return false;
		}
	}

	private static final int TWO_MINUTES = 1000 * 60 * 2;

	protected boolean isBetterLocation(Location location,
			Location currentlyBestLocation) {
		if (currentlyBestLocation == null) {
			// A new location is always better than no location
			return true;
		}

		// Check whether the new location fix is newer or older
		long timeDelta = location.getTime() - currentlyBestLocation.getTime();
		boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
		boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
		boolean isNewer = timeDelta > 0;

		// If it's been more than two minutes since the current location, use
		// the new location
		// because the user has likely moved
		if (isSignificantlyNewer) {
			return true;
			// If the new location is more than two minutes older, it must be
			// worse
		} else if (isSignificantlyOlder) {
			return false;
		}

		// Check whether the new location fix is more or less accurate
		int accuracyDelta = (int) (location.getAccuracy() - currentlyBestLocation
				.getAccuracy());
		boolean isLessAccurate = accuracyDelta > 0;
		boolean isMoreAccurate = accuracyDelta < 0;
		boolean isSignificantlyLessAccurate = accuracyDelta > 200;

		// Check if the old and new location are from the same provider
		boolean isFromSameProvider = isSameProvider(location.getProvider(),
				currentlyBestLocation.getProvider());

		// Determine location quality using a combination of timeliness and
		// accuracy
		if (isMoreAccurate) {
			return true;
		} else if (isNewer && !isLessAccurate) {
			return true;
		} else if (isNewer && !isSignificantlyLessAccurate
				&& isFromSameProvider) {
			return true;
		}
		return false;
	}

	/** Checks whether two providers are the same */
	private boolean isSameProvider(String provider1, String provider2) {
		if (provider1 == null) {
			return provider2 == null;
		}
		return provider1.equals(provider2);
	}
}
